<?php

namespace support\Strategy;

use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use ZipArchive;
use RuntimeException;

/**
 *  文件操作类
 * @author bh
 * @since 2025/01/20
 * @method fileExists(string $path)  检测文件是否存在
 * @method mkDir(string $path)  创建目录
 * @method delDir(string $path)  创建目录
 * @method handleFile(string $old_path, string $new_path, string $type)  文件操作(复制/移动)
 * @method handleDir(string $old_path, string $new_path, string $type)  文件夹操作(复制/移动)
 * @method unlinkFile(string $path)  删除文件
 */
class FileStrategy
{

    /**
     * 检测文件是否存在
     * @param $path string 文件路径
     * @return bool
     */
    public static function fileExists(string $path): bool
    {
        return file_exists($path);
    }

    /**
     * 创建目录
     * @param string $path 目录路径
     * @return bool
     */
    public static function mkDir(string $path)
    {
        $path = rtrim($path, '/') . '/';
        if (!is_dir($path)) {
            if (mkdir($path, 0700) == false) {
                return false;
            }
            return true;
        }
        return true;
    }

    /**
     * 删除目录
     * @param $path string 目录路径
     * @return bool
     */
    public static function delDir($path)
    {
        if (!file_exists($path)) return false;
        $dir = opendir($path);
        while ($fileName = readdir($dir)) {
            $file = $path . '/' . $fileName;
            if ($fileName != '.' && $fileName != '..') {
                if (is_dir($file)) {
                    self::delDir($file);
                } else {
                    unlink($file);
                }
            }
        }
        closedir($dir);
        return rmdir($path);
    }

    /**
     * 文件操作(复制/移动)
     * @param string $old_path 指定要操作文件路径(需要含有文件名和后缀名)
     * @param string $new_path 指定新文件路径（需要新的文件名和后缀名）
     * @param string $type 文件操作类型
     * @param boolean $overWrite 是否覆盖已存在文件
     * @param array $ignore 按后缀名过滤
     * @return boolean
     */
    public static function handleFile(string $old_path, string $new_path, string $type = 'copy', bool $overWrite = false, array $ignore = [])
    {
        $old_path = self::dirReplace($old_path);
        $new_path = self::dirReplace($new_path);
        if (file_exists($new_path) && $overWrite = false) {
            return FALSE;
        } else if (file_exists($new_path) && $overWrite = true) {
            self::unlinkFile($new_path);
        }

        $extension = pathinfo($old_path, PATHINFO_EXTENSION);
        if ($ignore && $extension && in_array($extension, $ignore)) {
            return true;
        }

        $aimDir = dirname($new_path);
        self::createDir($aimDir);
        switch ($type) {
            case 'copy':
                return copy($old_path, $new_path);
            case 'move':
                return @rename($old_path, $new_path);
        }
    }

    /**
     * 文件夹操作(复制/移动)
     * @param string $old_path 指定要操作文件夹路径
     * @param string $aimDir 指定新文件夹路径
     * @param string $type 操作类型
     * @param boolean $overWrite 是否覆盖文件和文件夹
     * @param array $ignore 按目录名过滤
     * @return boolean
     */
    public static function handleDir(string $old_path, string $new_path, string $type = 'copy', bool $overWrite = FALSE, array $ignore = [])
    {
        $new_path = self::checkPath($new_path);
        $old_path = self::checkPath($old_path);
        if (!is_dir($old_path)) return FALSE;

        if (!file_exists($new_path)) self::createDir($new_path);

        $dirHandle = opendir($old_path);

        if (!$dirHandle) return FALSE;

        $boolean = TRUE;

        while (FALSE !== ($file = readdir($dirHandle))) {
            if ($file == '.' || $file == '..') continue;

            if (!is_dir($old_path . $file)) {
                $boolean = self::handleFile($old_path . $file, $new_path . $file, $type, $overWrite);
            } else {
                if ($ignore && in_array($file, $ignore)) {
                    break;
                }
                self::handleDir($old_path . $file, $new_path . $file, $type, $overWrite);
            }
        }
        switch ($type) {
            case 'copy':
                closedir($dirHandle);
                return $boolean;
            case 'move':
                closedir($dirHandle);
                return @rmdir($old_path);
        }
    }

    /**
     * 替换相应的字符
     * @param string $path 路径
     * @return string
     */
    public static function dirReplace(string $path): string
    {
        return str_replace('//', '/', str_replace('\\', '/', $path));
    }

    /**
     * 文件保存路径处理
     * @param string $path
     * @return string
     */
    public static function checkPath($path): string
    {
        return (preg_match('/\/$/', $path)) ? $path : $path . '/';
    }

    /**
     * 创建多级目录
     * @param string $dir
     * @param int $mode
     * @return boolean
     */
    public static function createDir(string $dir, int $mode = 0777): bool
    {
        return is_dir($dir) or (self::createDir(dirname($dir)) and mkdir($dir, $mode));
    }

    /**
     * 删除文件
     * @param string $path
     * @return boolean
     */
    public static function unlinkFile(string $path): bool
    {
        $path = self::dirReplace($path);
        if (file_exists($path)) {
            return unlink($path);
        }
        return true;
    }

    /**
     * 压缩文件夹
     * @param $folderPath string 要压缩的文件夹路径
     * @param $zipFilePath string 输出的 ZIP 文件路径
     * @return
     */
    public static function zipFolder(string $folderPath, string $zipFilePath): string
    {
        // 创建一个新的 ZipArchive 对象
        $zip = new ZipArchive();
        // 创建 ZIP 文件
        if ($zip->open($zipFilePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
            throw  new RuntimeException("无法打开 <$zipFilePath>");
        }
        // 递归添加文件夹内容到 ZIP 文件
        $folderPath = rtrim($folderPath, '/') . '/';
        $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($folderPath));
        foreach ($iterator as $file) {
            // 跳过目录
            if (!$file->isDir()) {
                // 获取相对路径
                $filePath = $file->getRealPath();
                $relativePath = substr($filePath, strlen($folderPath));

                // 添加文件到 ZIP
                $zip->addFile($filePath, $relativePath);
            }
        }
        // 关闭 ZIP 文件
        $zip->close();
        return  $zipFilePath;
    }

}